﻿Imports System.Windows.Documents
Imports System.Windows.Controls
Imports System.Runtime.CompilerServices
Imports System.Windows
Imports System.Security.Permissions
Imports System.Windows.Threading

Namespace Reporting
    Public Class ReportingEngine
        Dim _reportElements As New List(Of UserControl)
        Public ReadOnly Property ReportElements As List(Of UserControl)
            Get
                Return _reportElements
            End Get
        End Property

        Public Property Template As IReportTemplate
        Public Property ElementMargin As Thickness = New Thickness(10)
        Public Property PageWidth As Integer = 793.76
        Public Property PageHeight As Integer = 1122.56

        Public ReadOnly Property Report As FixedDocument
            Get

                Dim fd As New FixedDocument
                Dim pc As New PageContent
                Dim fp As New FixedPage With {.Width = PageWidth, .Height = PageHeight}

                For Each item In ReportElements
                    DoRender(item)
                Next

                fd.Pages.Add(pc)
                pc.Child = fp
                Dim templatePage As IReportTemplate = Template.GetType.Assembly.CreateInstance(Template.GetType.FullName)
                templatePage.UserControl.Width = fp.Width
                For Each item In ReportElements
                    DoRender(item)

                    If CheckCanFit(item, templatePage) = True Then
                        templatePage.AddElement(item)
                    Else
                        'Add Current Report Document
                        fp.Children.Add(templatePage.UserControl)
                        'New Page Content
                        pc = New PageContent
                        'New Fixed Page
                        fp = New FixedPage With {.Width = PageWidth, .Height = PageHeight}
                        'New ReportDocuement
                        templatePage = Template.GetType.Assembly.CreateInstance(Template.GetType.FullName)
                        templatePage.UserControl.Width = fp.Width

                        'Wire them up
                        'Add Page to PageContent
                        pc.Child = fp
                        'Add PageContent to FixedDocument
                        fd.Pages.Add(pc)

                        templatePage.AddElement(item)
                    End If
                Next

                fp.Children.Add(templatePage)
                DoEvents()
                Return fd

            End Get
        End Property

        Public Sub DoRender(uc As FrameworkElement)
            uc.Measure(New Size(Double.MaxValue, Double.MaxValue))
            uc.Arrange(New Rect(New Point(0, 0), uc.DesiredSize))
            uc.UpdateLayout()
        End Sub

        Private Function CheckCanFit(element As FrameworkElement, page As IReportTemplate) As Boolean
            DoRender(page.UserControl)
            If element.ActualHeight + element.Margin.Top + element.Margin.Bottom + page.UserControl.ActualHeight < PageHeight Then
                'Page is less than height so can fit
                Return True
            Else
                Return False
            End If
        End Function

        Private Function CreateType(Of T As New)() As T
            Return New T()
        End Function

        <SecurityPermissionAttribute(SecurityAction.Demand, Flags:=SecurityPermissionFlag.UnmanagedCode)>
        Public Sub DoEvents()
            Dim frame As New DispatcherFrame()
            Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, New DispatcherOperationCallback(Function(f As Object)
                                                                                                                        CType(f, DispatcherFrame).Continue = False

                                                                                                                        Return Nothing
                                                                                                                    End Function), frame)
            Dispatcher.PushFrame(frame)
        End Sub
    End Class
End Namespace
